<?php
//
//  Copyright (c) 2009 Facebook
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

//
// This file defines the interface iXHProfRuns and also provides a default
// implementation of the interface (class XHProfRuns).
//

/**
 * iXHProfRuns interface for getting/saving a XHProf run.
 *
 * Clients can either use the default implementation,
 * namely XHProfRuns_Default, of this interface or define
 * their own implementation.
 *
 * @author Kannan
 */

if (!interface_exists('iXHProfRuns')) {
    interface iXHProfRuns {

        /**
         * Returns XHProf data given a run id ($run) of a given
         * type ($type).
         *
         * Also, a brief description of the run is returned via the
         * $run_desc out parameter.
         */
        public function get_run($run_id, $type, &$run_desc);

        /**
         * Save XHProf data for a profiler run of specified type
         * ($type).
         *
         * The caller may optionally pass in run_id (which they
         * promise to be unique). If a run_id is not passed in,
         * the implementation of this method must generated a
         * unique run id for this saved XHProf run.
         *
         * Returns the run id for the saved XHProf run.
         *
         */
        public function save_run($xhprof_data, $type, $run_id = null);
    }
}


/**
 * XHProfRuns_Default is the default implementation of the
 * iXHProfRuns interface for saving/fetching XHProf runs.
 *
 * It stores/retrieves runs to/from a filesystem directory
 * specified by the "xhprof.output_dir" ini parameter.
 *
 * @author Kannan
 */

if (!class_exists('XHProfRuns_Default')) {
    class XHProfRuns_Default implements iXHProfRuns {

        private $dir = '';
        private $suffix = 'xhprof';

        private function gen_run_id($type) {
            return uniqid();
        }

        private function file_name($run_id, $type) {

            $file = "$run_id.$type." . $this->suffix;

            if (!empty($this->dir)) {
                $file = $this->dir . "/" . $file;
            }
            return $file;
        }

        public function __construct($dir = null) {

            // if user hasn't passed a directory location,
            // we use the xhprof.output_dir ini setting
            // if specified, else we default to the directory
            // in which the error_log file resides.

            if (empty($dir)) {
                $dir = ini_get("xhprof.output_dir");
                if (empty($dir)) {

                    $dir = sys_get_temp_dir();

                    xhprof_error("Warning: Must specify directory location for XHProf runs. ".
                        "Trying {$dir} as default. You can either pass the " .
                        "directory location as an argument to the constructor ".
                        "for XHProfRuns_Default() or set xhprof.output_dir ".
                        "ini param.");
                }
            }
            $this->dir = $dir;
        }

        public function get_orig_data($run_id, $type, &$run_desc) {
            $file_name = $this->file_name($run_id, $type);

            if (!file_exists($file_name)) {
                xhprof_error("Could not find file $file_name");
                $run_desc = "Invalid Run Id = $run_id";
                return null;
            }

            $contents = file_get_contents($file_name);
            $run_desc = "XHProf Run (Namespace=$type)";
            $data = unserialize($contents);
            $filter = !empty($_GET['filter']) ? explode('|', $_GET['filter']) : [];

            if ($filter) {
                foreach ($data['data'] as $key => $val) {
                    $t = explode('==>', $key);
                    foreach ($filter as $f) {
                        if (strncasecmp($t[0], $f, strlen($f)) == 0 ||
                            (isset($t[1]) && strncasecmp($t[1], $f, strlen($f)) == 0)
                        ) {
                            unset($data['data'][$key]);
                        }
                    }
                }
            }
            return $data;

        }

        public function get_run($run_id, $type, &$run_desc) {
            $orig_data = $this->get_orig_data($run_id, $type, $run_desc);
            return $orig_data['data'];
        }

        public function get_runtime_vars($run_id, $type, &$run_desc) {
            $orig_data = $this->get_orig_data($run_id, $type, $run_desc);
            unset($orig_data['data']);
            return $orig_data;
        }

        public function save_run($xhprof_data, $type, $run_id = null) {

            // Use PHP serialize function to store the XHProf's
            // raw profiler data.
            $xhprof_data = serialize($xhprof_data);

            if ($run_id === null) {
                $run_id = $this->gen_run_id($type);
            }

            $file_name = $this->file_name($run_id, $type);
            $file = fopen($file_name, 'w');

            if ($file) {
                fwrite($file, $xhprof_data);
                fclose($file);
            } else {
                xhprof_error("Could not open $file_name\n");
            }

            // echo "Saved run in {$file_name}.\nRun id = {$run_id}.\n";
            return $run_id;
        }

        function list_runs($name = null) {
            if (is_dir($this->dir)) {
                echo "<hr/>Existing runs:\n<ul>\n";
                $pattern = sprintf("%s/*.%s%s", $this->dir, ($name ? $name . '.' : ''), $this->suffix);
                $files = glob($pattern);
                usort($files, create_function('$a,$b', 'return filemtime($b) - filemtime($a);'));
                foreach ($files as $file) {
                    list($run,$source) = explode('.', basename($file));
                    echo '<li><a href="' . htmlentities($_SERVER['SCRIPT_NAME'])
                        . '?run=' . htmlentities($run) . '&source='
                        . htmlentities($source) . '">'
                        . htmlentities(basename($file)) . "</a><small> "
                        . date("Y-m-d H:i:s", filemtime($file)) . "</small></li>\n";
                }
                echo "</ul>\n";
            }
        }
    }
}
